[RFC] Add ChannelMonitor::get_justice_txs for simplified watchtower integration#4453
[RFC] Add ChannelMonitor::get_justice_txs for simplified watchtower integration#4453FreeOnlineUser wants to merge 4 commits intolightningdevkit:mainfrom
Conversation
|
👋 Hi! This PR is now in draft status. |
6993e59 to
d6c7903
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4453 +/- ##
==========================================
- Coverage 85.93% 85.93% -0.01%
==========================================
Files 159 159
Lines 104693 104736 +43
Branches 104693 104736 +43
==========================================
+ Hits 89972 90001 +29
- Misses 12213 12222 +9
- Partials 2508 2513 +5
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| /// Contains the current and previous counterparty commitment(s). With splicing, | ||
| /// there may be multiple entries per commitment number (one per funding scope). | ||
| /// Pruned to remove entries more than one revocation old. | ||
| latest_counterparty_commitment_txs: Vec<CommitmentTransaction>, |
There was a problem hiding this comment.
This should be in the funding scope to support splicing. Also should probably just be cur_... and prev_... to match existing API pattern rather than a vec that is max-size 2.
There was a problem hiding this comment.
Moved to FundingScope as cur/prev fields. TLV 13/15 on FundingScope, 39/41 on the outer scope for backwards compat.
lightning/src/util/test_utils.rs
Outdated
| Err(_) => break, | ||
| } | ||
| } | ||
| // Use the simplified get_justice_txs API |
There was a problem hiding this comment.
Claude loves to leave comments about how it changed the code that are totally useless - reading the code I don't care what changes claude made in the past, I care if there's something useful to know now. Probably can just drop this.
There was a problem hiding this comment.
Removed, and noted for future.
| /// | ||
| /// Returns a list of [`JusticeTransaction`]s, each containing a fully signed | ||
| /// transaction and metadata about the revoked commitment it punishes. | ||
| pub fn get_justice_txs( |
There was a problem hiding this comment.
This API doesn't really make sense. There's no information on when I should call this or when the monitor "knows about" a revoked tx. We probably want to do soemthing like the old API where you can fetch revoked transactions in relation to a specific monitor update.
There was a problem hiding this comment.
Replaced with sign_initial_justice_tx() for persist_new_channel and sign_justice_txs_from_update(update) for update_persisted_channel. Each is tied to a specific point in the persistence pipeline.
d6c7903 to
9af37e1
Compare
Adds sign_initial_justice_tx() and sign_justice_txs_from_update() to ChannelMonitor, allowing Persist implementors to obtain signed justice transactions without maintaining external state. Storage uses cur/prev counterparty commitment fields on FundingScope, matching the existing pattern and supporting splicing. Simplifies WatchtowerPersister in test_utils by removing the manual queue and signing logic. Addresses feedback from lightningdevkit/ldk-node#813 and picks up the intent of lightningdevkit#2552.
9af37e1 to
034c377
Compare
TheBlueMatt
left a comment
There was a problem hiding this comment.
Thanks, I think this basically looks good!
| let mut result = Vec::new(); | ||
| for commitment_tx in &to_sign { | ||
| if let Some(jtx) = | ||
| self.try_sign_justice_tx(commitment_tx, feerate_per_kw, destination_script.clone()) |
There was a problem hiding this comment.
Should we also build justice transactions for all the potential HTLC transactions? I assume we can...
There was a problem hiding this comment.
Added. try_sign_justice_txs now iterates nondust_htlcs() on the revoked commitment, builds a spending tx for each HTLC output, and signs with sign_justice_revoked_htlc. Witness follows the same pattern as RevokedHTLCOutput in package.rs. Dust HTLCs are skipped when fee exceeds value.
Also added test_justice_tx_htlc_from_monitor_updates which routes a payment, captures the commitment with a pending HTLC, revokes it, and verifies the watchtower gets justice txs for both to_local and the HTLC output.
| funding.prev_counterparty_commitment_tx = | ||
| funding.cur_counterparty_commitment_tx.take(); | ||
| funding.cur_counterparty_commitment_tx = Some(commitment_tx); | ||
| } |
There was a problem hiding this comment.
I assume we can/should do this?
| } | |
| } else { | |
| debug_assert!(false); | |
| } |
Extends try_sign_justice_txs to also sweep HTLC outputs when a counterparty broadcasts a revoked commitment. For each non-dust HTLC, builds a spending transaction and signs it with sign_justice_revoked_htlc. Updates WatchtowerPersister to store multiple justice txs per revoked commitment (Vec<Transaction> instead of single Transaction). Addresses review feedback from TheBlueMatt on PR lightningdevkit#4453.
Verifies that WatchtowerPersister produces justice transactions for both to_local and in-flight HTLC outputs when a commitment with pending HTLCs is revoked. Checks each justice tx spends a different output and pays to the destination script.
FreeOnlineUser
left a comment
There was a problem hiding this comment.
Updated with both changes. API is now sign_initial_justice_txs / try_sign_justice_txs (plural) since they return a Vec covering to_local + HTLCs. WatchtowerPersister stores Vec<Transaction> per revoked txid accordingly.
Draft PR for design feedback. Implements the approach @TheBlueMatt suggested in lightningdevkit/ldk-node#813 and in review of #2552: move justice tx state tracking inside
ChannelMonitorsoPersistimplementors don't need external queues.What this does
Adds a single method that replaces the current 3-step dance of
counterparty_commitment_txs_from_update()+ manual queue +sign_to_local_justice_tx():Internally,
ChannelMonitorImplstores recent counterpartyCommitmentTransactions (populated during update application, pruned to entries within one revocation of current).get_justice_txschecks which have revocation secrets available, builds and signs the justice tx, and returns the result.Changes
channelmonitor.rs: newJusticeTransactionstruct,latest_counterparty_commitment_txsfield, TLV 39 serialization (optional, backwards-compatible),get_justice_txs()methodtest_utils.rs: simplifiedWatchtowerPersister(removedJusticeTxData,unsigned_justice_tx_dataqueue,form_justice_data_from_commitment)Splice handling
Pruning uses commitment numbers, not entry count. During a splice, multiple entries share the same commitment number (one per funding scope) and all are retained.
Backwards compatibility
counterparty_commitment_txs_from_update()andsign_to_local_justice_tx()APIs unchanged.Design questions
Looking for input on these before moving out of draft:
Signed vs unsigned return? Currently returns fully signed transactions. Returning unsigned gives callers more flexibility on fee choice at broadcast time. Preference?
HTLC outputs? This only covers
to_localjustice. HTLC-timeout and HTLC-success outputs on revoked commitments are not included. Worth adding here, or separate follow-up?Feerate source? Caller provides
feerate_per_kw. An alternative would be accepting a fee estimator. Any preference?Dust filtering? If the revokeable output is dust,
revokeable_output_index()returnsNoneand the entry is skipped silently. Right behavior, or surface this to the caller?